package com.gotop.auth.controller;

import com.gotop.auth.granter.ITokenGranter;
import com.gotop.auth.granter.TokenGranterBuilder;
import com.gotop.auth.service.IClientService;
import com.gotop.auth.utils.TokenUtil;
import com.gotop.common.base.BaseController;
import com.gotop.common.config.redis.RedisUtil;
import com.gotop.common.constant.Kv;
import com.gotop.common.enums.EnumTokenGranter;
import com.gotop.common.exception.BaseException;
import com.gotop.common.redis.CacheNames;
import com.gotop.common.utils.R;
import com.gotop.common.utils.WebUtil;
import com.gotop.common.utils.auth.*;
import com.gotop.entity.system.Client;
import com.gotop.info.UserInfo;
import com.wf.captcha.SpecCaptcha;
import io.jsonwebtoken.Claims;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @program: cem
 * @description: 授权认证
 * @author: superfish
 * @create: 2020-12-17 14:59
 **/
@RestController
@AllArgsConstructor
@RequestMapping("authorization")
@Api(value = "用户授权认证", tags = "授权接口")
public class AuthController extends BaseController {

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private IClientService iClientService;

    @PostMapping("token")
    @ApiOperation(value = "获取认证token", notes = "传入租户ID:tenantId,账号:account,密码:password")
    public R<AuthInfo> token(@ApiParam(value = "授权类型", required = true) @RequestParam(defaultValue = "password", required = false) String grantType,
                             @ApiParam(value = "刷新令牌") @RequestParam(required = false) String refreshToken,
                             @ApiParam(value = "租户ID", required = true) @RequestParam(defaultValue = "000000", required = false) String tenantId,
                             @ApiParam(value = "账号") @RequestParam(required = false) String account,
                             @ApiParam(value = "密码") @RequestParam(required = false) String password) {

        String userType = String.valueOf(WebUtil.getRequest().getHeader(TokenUtil.USER_TYPE_HEADER_KEY));
        TokenParameter tokenParameter = new TokenParameter();
        tokenParameter.getArgs().set("tenantId", tenantId)
                .set("account", account)
                .set("password", password)
                .set("grantType", grantType)
                .set("refreshToken", refreshToken)
                .set("userType", userType);

        ITokenGranter granter = TokenGranterBuilder.getGranter(grantType);
        UserInfo userInfo = granter.grant(tokenParameter);

        if (userInfo == null || userInfo.getUser() == null) {
            return R.fail(TokenUtil.USER_NOT_FOUND);
        }

        return R.data(TokenUtil.createAuthInfo(userInfo));
    }

    @GetMapping("/sdk")
    @ApiOperation(value = "sdk token获取")
    public R sdk(@ApiParam(value = "账号") @RequestParam(required = false) String clientId,
                 @ApiParam(value = "密码") @RequestParam(required = false) String clientSecret,
                 @ApiParam(value = "授权类型") @RequestParam(required = false) String grantType,
                 @ApiParam(value = "刷新token") @RequestParam(required = false) String refreshToken,
                 HttpServletRequest request) {
        final String ip = WebUtil.getIP(request);
        try {
            if (EnumTokenGranter.SDK_TOKEN_GRANTER.getMsg().equals(grantType)) {
                // 验证
                final Client client = iClientService.check(clientId, clientSecret, grantType, ip);
                // token创建
                final Map<String, Object> tokenMap = getTokenMap(client);
                return R.data(tokenMap);
            } else if (StringUtils.isNoneBlank(grantType, refreshToken) && grantType.equals(TokenConstant.REFRESH_TOKEN)) {
                final Claims claims = SecureUtil.parseJWT(refreshToken);
                final String tokenType = String.valueOf(Objects.requireNonNull(claims).get(TokenConstant.TOKEN_TYPE));
                if (tokenType.equals(TokenConstant.REFRESH_TOKEN)) {
                    final String clientId_token = String.valueOf(Objects.requireNonNull(claims).get(TokenConstant.CLIENT_ID));
                    final Client client = iClientService.selectByClientId(clientId_token);
                    final Map<String, Object> tokenMap = getTokenMap(client);
                    return R.data(tokenMap);
                }
            }
        } catch (BaseException e) {
            return R.data(e);
        }
        return R.fail("操作失败");
    }

    /**
     * token创建
     *
     * @param client
     */
    private Map<String, Object> getTokenMap(Client client) {
        Map<String, Object> map = new HashMap<>();
        TokenInfo accessToken = SecureUtil.createJWT(client.getClientId(), client.getAccessTokenValidity(), client.getRefreshTokenValidity(), "audience", "issuser", TokenConstant.ACCESS_TOKEN);
        map.put("access_token", accessToken.getToken());
        map.put("access_token_expires_in", accessToken.getExpire());
        final String string = TokenUtil.createRefreshToken(client.getClientId(), client.getAccessTokenValidity(), client.getRefreshTokenValidity()).getToken();
        map.put("refresh_token", string);
        map.put("refresh_token_expires_in", client.getRefreshTokenValidity());
        return map;
    }

    @GetMapping("/captcha")
    @ApiOperation(value = "获取验证码")
    public R<Kv> captcha() {
        SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 5);
        String verCode = specCaptcha.text().toLowerCase();
        String key = UUID.randomUUID().toString();
        // 存入redis并设置过期时间为30分钟
        redisUtil.set(CacheNames.CAPTCHA_KEY + key, verCode, 30L, TimeUnit.MINUTES);
        // 将key和base64返回给前端
        return R.data(Kv.init().set("key", key).set("image", specCaptcha.toBase64()));
    }
}
